import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;


public class NetSim {

	static final int TOKENMSGID = 1000;
	
	boolean SISinit = false;
	
	//I hate typing System.out.println
	public static void p(String s) {
		System.out.println(s);
	}
	
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		if(args.length < 1) {
			p("Usage: java NetSim <pnml-file> [SIS-Server-IP] [SIS-Server-Port]");
			return;
		}
		
		PnmlData pnml = new PnmlData(args[0]);
		
		p(pnml.toString());
		String server = "127.0.0.1";
		String port = "7999";
		if(args.length > 1)
			server = args[1];
		if(args.length > 2)
			port = args[2];
		NetSim nsInst = new NetSim();
		SISThread inThr = nsInst.new SISThread(pnml, server, port);
		inThr.start();
		while(inThr.ME == null) Thread.yield();
		SISSender outThr = nsInst.new SISSender(inThr.S, inThr.ME);
		outThr.start();
		new PNSimFrame(pnml, inThr, outThr);//outThr);
	}

	class SISSender extends Thread {
		final Socket S;
		final MsgEncoder ME;
		public KeyValueList nextMsg = null;
		
		public SISSender(Socket s, MsgEncoder me) {
			S = s;
			ME = me;
			this.setDaemon(true);
		}
		public void run() {
			while(true) {
				if(nextMsg != null)
					try {
						ME.sendMsg(nextMsg, S.getOutputStream());
					} catch (IOException e) {
						e.printStackTrace();
					}
				nextMsg = null;
				Thread.yield();
			}
		}
	}
	
	class SISThread extends Thread {
		public final PnmlData SimData;
		public final String ServerAddr;
		public final String ServerPort;
		public final Socket S; 
		public KeyValueList msgToSend = null;
		public MsgEncoder ME = null;
		public boolean Refresh = false;
		
		public SISThread(PnmlData data, String server, String port) {
			SimData = data;
			ServerAddr = server;
			ServerPort = port;
			try {
				S = new Socket(server,Integer.parseInt(port));
			} catch (Exception e) {				
				e.printStackTrace();
				throw new RuntimeException("Shutting down, server error.");
			}
			this.setDaemon(true);
		}
		
		public void run() {
	        try{
	            ME = new MsgEncoder();
	            MsgDecoder md = new MsgDecoder(S.getInputStream());
	            KeyValueList l = new KeyValueList();
				p("Connecting to server (ID=23)...");
	            l.addPair("MsgID", "23");
	            l.addPair("Name", SimData.getNetId());
	            ME.sendMsg(l, S.getOutputStream());
	            p("Init sent (23)");
	            while(true){
	                p("Listening for message...");
	                l = md.getMsg();
	                if(l == null)
	                	throw new RuntimeException("The connection was closed.");
	                p("debug: got msg. " + l);
	                int msgID = Integer.parseInt(l.getValue("MsgID"));
					p("Message Received. ID="+msgID);
					 
					 switch(msgID){
						 case 22: 
							 p("KillComponent (22) not expected, but still will die.");
							 System.exit(-1);
							 break;
						 case 25:
							p("Received Deactivate Component (25).  Shutting down.");
							System.exit(0);
						 break;
						 case 26: 
							p("Init ACK from server. (ID=26)");
							SISinit = true;
						 break;
						 case TOKENMSGID:
							 if(!SISinit)
								 throw new RuntimeException("No INIT(26) received from SIS server.");
							 if(l.lookupKey("NetID") >= 0 && l.getValue("NetID").length() > 0 
							    && !SimData.getNetId().equals(l.getValue("NetID")))
									 break;
							 String pid = l.getValue("PlaceID");
							 Object p = SimData.getNodeById(pid);
							 if(p == null || p.getClass() != PNPlace.class)
								 break;
							 ((PNPlace)p).HasToken = true;
							 Refresh = true;
						 break;
						 
						 default: System.out.println("NetSim does not recognize server msg: ID="+msgID);break;
					 }
	            }
	        }
	        catch (Exception e) {
				System.out.println("The PN Simulator shut down due to errors.");
	            System.out.println("An exception occurred: "+e);
				e.printStackTrace();
	        }
		}
	}
}
